home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 1 / PC Actual CD 01.iso / trucos / laborat / lab.cd next >
Encoding:
Text File  |  1995-01-03  |  34.3 KB  |  1,722 lines

  1.  
  2. LOS TRUCOS DEL LABORATORIO
  3.  
  4.  
  5. PROGRAMAS PROFESIONALES
  6.  
  7. Los técnicos del Laboratorio de PC Actual evaluamos continuamente gran
  8. cantidad de programas, entre los que se encuentran los realizados por
  9. nuestros lectores. Por este motivo observamos ciertos detalles que
  10. hacen que los programas de nuestros lectores no lleguen a tener ese
  11. toque de profesionalidad tan deseable en toda aplicación.
  12.  
  13. Cualquiera que haya programado alguna vez en C o Pascal y haya
  14. necesitado hacer uso de funciones gráficas, se habrá visto obligado a
  15. incluir en el mismo directorio en que se encontraba el programa
  16. definitivo los conocidos ficheros BGI (Borland Graphics Interface).
  17. Esta no es, desde luego, una solución muy profesional, puesto que
  18. cualquier descuido con estos BGI daría al traste con nuestro programa.
  19.  
  20. Uno de los detalles más sencillos de conseguir es la introducción de
  21. dichos drivers de pantalla (.BGI) y de fuentes de letra (.CHR), dentro
  22. de los ejecutables realizados con compiladores de Borland (Turbo
  23. Pascal, Borland Pascal, Turbo C, etc).
  24.  
  25. El siguiente ejemplo muestra cómo introducir el driver para tarjetas
  26. VGA (EgaVga.bgi) dentro de un ejecutable en programas de Pascal,
  27. haciendo uso de la utilidad BINOBJ (que por defecto se encuentra en el
  28. directorio «\tp\utils»), utilidad que convierte ficheros binarios en
  29. código objeto. Para ello teclearemos:
  30.  
  31. BINOBJ egavga.bgi egavga egavga
  32.  
  33. Tras esto obtendremos un fichero «egavga.obj». Para utilizarlo
  34. bastará con introducirlo dentro del programa en Pascal, para lo que
  35. crearemos la siguiente unidad:
  36.  
  37. Unit tarjetas;
  38.  
  39. Interface
  40.  
  41. Procedure EgaVga;
  42.  
  43. Implementation
  44.  
  45. Procedure EgaVga;
  46.  
  47. External;
  48.  
  49. {$L EgaVga.OBJ}
  50.  
  51. End.
  52.  
  53. TEXR = En nuestro programa, en la declaración de unidades empleadas
  54. (línea «USES»), tendremos que incluir entonces la unidad «tarjetas»
  55. así definida. Para inicializar el modo gráfico procederemos de la
  56. siguiente forma:
  57.  
  58. Procedure IniciaGraficos; {Inicia modo gráfico}
  59.  
  60. Var GD,GM :Integer;
  61.  
  62. Begin
  63.  
  64. If RegisterBGIDriver (@EgaVga) <0 Then Halt (1);
  65.  
  66. GD := VGA;
  67.  
  68. GM := VGAHi;
  69.  
  70. InitGraph(GD,GM,'');
  71.  
  72. If GraphResult <0 then Halt(1);
  73.  
  74. End;
  75.  
  76. Para incorporar los archivos de las fuentes (.chr) procederemos de
  77. manera similar. Como podemos ver, esto tiene como ventaja que una vez
  78. hayamos realizado la conversión a obj y tengamos la unidad «tarjetas»,
  79. no será necesario volverlas a escribir, y prodremos usarlo en todos
  80. nuestros programas.
  81.  
  82. Para llevar esto a cabo en los programas en Turbo C actuaremos de modo
  83. similar, sabiendo que Borland incluye con sus compiladores de C la
  84. utilidad «bgiobj», que nos permite transformar ficheros BGI y CHR (los
  85. que contienen los tipos de letras) en ficheros OBJ, directamente
  86. enlazables con nuestro programa. Para este paso tenemos que dar la
  87. orden BGIOBJ «fichero» (donde «fichero» es cualquier archivo con
  88. extensión BGI o CHR).
  89.  
  90. A continuación podemos incluir los ficheros objeto así generados en
  91. alguna de las librerías (por ejemplo, en graphics.lib). Para ello
  92. daremos la orden
  93.  
  94. TLIB graphics + «fichero objeto» + «fichero objeto» + ...
  95.  
  96. A la hora de montar el programa tenemos que indicarle al compilador
  97. que vamos a emplear estos ficheros, para lo cual es necesario utilizar
  98. las funciones «registerbgidriver()» y «registerbgifont()» en el
  99. programa principal antes de hacer la llamada a «initgraph()». Estas
  100. funciones usan como argumento un nombre simbólico definido en
  101. graphics.h (EGAVGA_driver, ATT_driver, triplex_font, gothic_font,
  102. etc).
  103.  
  104. Nota: Si no hemos añadido los ficheros objeto a la librería gráfica,
  105. es necesario compilarlos con el programa fuente (o añadirlos en el
  106. fichero de proyecto). Por ejemplo,
  107.  
  108. TCC «programa_fuente» GRAPHICS.LIB EGAVGA.OBJ TRIP.OBJ
  109.  
  110.  
  111.  
  112. PROGRAMACION DE LA VGA EN MODO 320x200x256 (I)
  113.  
  114. Compiladores como Turbo Pascal o Turbo C (o sus hermanos mayores
  115. Borland Pascal y Borland C/C++) ofrecen unos drivers de los que ya
  116. hemos hablado en numerosas ocasiones: los BGI (Borland Graphics
  117. Interface).
  118.  
  119. Con estos drivers en modo VGA podemos conseguir la resolución de 640 x
  120. 480 con 16 colores, con la ventaja de tener un gran número de
  121. funciones de dibujo y de salida de texto ya definidas. Este modo
  122. ofrece excelentes resultados para programas que requieran gran
  123. detalle, pero el problema surge cuando para nuestro objetivo
  124. necesitamos mayor número de colores, pues 16 suelen ser pocos. En
  125. este caso podemos recurrir al modo 320 x 200 con 256 colores.
  126. Perdemos resolución, pero lo podemos subsanar mediante un buen uso de
  127. la paleta de colores. En muchas ocasiones el cambio es preferible,
  128. por lo que este modo es muy recomendable para ser utilizado en juegos.
  129.  
  130. La desventaja que posee este modo frente al VGA estándar ofrecido por
  131. el driver BGI es que no disponemos de funciones de dibujo ni de salida
  132. de texto, lo cual en principio puede asustar. Pero os aseguramos que
  133. es más fácil de lo que parece, y desde estas líneas vamos a intentar
  134. solventar todos los problemas que pueden aparecer.
  135.  
  136. También os recordamos que, aunque no se ofrezcan con los paquetes de
  137. Borland, circulan por ahí gran cantidad de drivers BGI que sí soportan
  138. este modo y los modos de SVGA (alcanzando incluso los 1.024 x 768 con
  139. 256 colores). Es fácil encontrarlos en cualquiera de las BBS
  140. españolas (incluida por supuesto PC Actual BBS) con nombres tales como
  141. SVGABGI, VGA256 u otros similares. Pero contemos o no contemos con
  142. ellos podemos intentar acceder a la VGA a un nivel un poco más
  143. profundo, para conseguir más potencia y velocidad.
  144.  
  145. En caso de disponer de los BGI para acceder al modo 320 x 200 x 256,
  146. también conocido como modo 13h, simplemente inicializaremos como de
  147. costumbre con el comando «initgraph» (tanto en C como en Pascal). Si
  148. no disponemos de estos drivers o no necesitamos las funciones que nos
  149. ofrecen, podemos inicializar el modo utilizando el assembler, cargando
  150. el valor 13h en el registro ax, y llamando después a la interrupción
  151. 10. Por ejemplo, si estamos empleando Turbo Pascal podemos introducir
  152. el siguiente conjunto de instrucciones en ensamblador:
  153.  
  154. asm
  155.  
  156. mov ax,13h
  157.  
  158. int 10h
  159.  
  160. end;
  161.  
  162. Si empleamos Turbo C o cualquiera de sus «hermanos», la sección de
  163. código anterior queda como sigue:
  164.  
  165. asm {
  166.  
  167. mov ax,13h
  168.  
  169. int 10h
  170.  
  171. }
  172.  
  173. De esta forma, inicializaremos el modo 320 x 200 x 256. Una de las
  174. ventajas que ofrece este modo es que, para acceder a cualquier
  175. posición de memoria, sólo necesita un segmento de 64.000 bytes (320 x
  176. 200 x 1 byte de color por pixel). De esta forma, para iluminar el
  177. pixel de coordenadas x e y sólo tendremos que cargar un byte en la
  178. dirección de memoria A000h:y*320+x. Dicho byte corresponderá al valor
  179. del color con que lo queramos iluminar, entre 0 y 255.
  180.  
  181. Por ejemplo, un simple programa en Pascal que dibuje 256 líneas
  182. verticales, cada una de un color diferente, quedaría como sigue:
  183.  
  184. Program VGA;
  185.  
  186. Uses Crt;
  187.  
  188. Var x,y:integer;
  189.  
  190. Begin
  191.  
  192.  asm
  193.  
  194.  mov ax,13h
  195.  
  196.  int 10h
  197.  
  198.  end;
  199.  
  200.  For x:=0 to 255 do
  201.  
  202.  For y:= 0 to 199 do
  203.  
  204.  Mem[$A000:y*320+x]:=x;
  205.  
  206.  Repeat Until KeyPressed;
  207.  
  208. End.
  209.  
  210. La misma rutina en C nos quedaría como sigue:
  211.  
  212. # include <dos.h>
  213.  
  214. main ()
  215.  
  216. {
  217.  
  218.  int x, y;
  219.  
  220.  asm MOV AX, 0x13;
  221.  
  222.  asm INT 0x10;
  223.  
  224.  for (x = 0; x <= 255; x++)
  225.  
  226.  for (y = 0; y <= 199; y++)
  227.  
  228.  poke (0xA000, y*320 + x, x);
  229.  
  230.  getch();
  231.  
  232. }
  233.  
  234. Tras ejecutar cualquiera de los anteriores programas, cuando volvamos
  235. al DOS nos encontraremos con que tenemos el modo de vídeo cambiado y
  236. para volver al modo normal tendremos que ejecutar «mode co80». Para
  237. evitar esto, podemos volver al modo de texto normal dentro del propio
  238. programa. Para ello tenemos dos posibilidades: bien con la
  239. instrucción «textmode (lastmode)» (común en C y Pascal, en C pertenece
  240. a la librería conio.h y en Pascal a la unidad Crt) o bien usando de
  241. nuevo el ensamblador, cargando un 3 en ax y ejecutando la interrupción
  242. 10h.
  243.  
  244. Por este mes terminamos con la programación de la VGA en el modo 13,
  245. pero seguiremos en sucesivos números con este interesante tema. Y no
  246. olvidéis que si vosotros tenéis alguna rutina interesante también
  247. podéis mandarla y de paso conseguir algún que otro premio.
  248.  
  249.  
  250.  
  251. LA PROGRAMACION DE LA VGA 320x200x256 (II)
  252.  
  253. En el truco anterior iniciamos esta serie sobre la programación en uno
  254. de los modos de la VGA más potentes y sencillos al mismo tiempo. La
  255. posibilidad de usar 256 colores al mismo tiempo es algo que sobrepasa
  256. con creces la pérdida de resolución frente a los 640x480 con 16
  257. colores.
  258.  
  259. Por ello, vamos a seguir profundizando un poco más en el modo gráfico
  260. más empleado, el conocido entre los programadores como modo 13. En el
  261. número anterior explicamos la forma de inicializar la pantalla e
  262. iluminar un pixel de un color determinado. Dijimos que para iluminar
  263. el pixel de coordenadas x,y sólo hace falta cargar un byte en la
  264. dirección de memoria A000h:y*320+x. Dicho byte corresponderá al valor
  265. (de 0 a 255) del color empleado para iluminarlo. La forma más
  266. sencilla de hacer esto en Pascal es mediante la instrucción
  267. «Mem[$A000:y*320+x]:=c;», donde «c» es el color del que queremos
  268. ilumninar la posición x,y.
  269.  
  270. También es posible hacerlo de forma inversa, es decir, escribir
  271. «c:=Mem[$A000:y*320+x];». Con esta simple instrucción obtendremos el
  272. color del pixel de coordenadas x,y. Con esto hemos conseguido simular
  273. las instrucciones «PutPixel» y «GetPixel» proporcionadas con los
  274. drivers BGI.
  275.  
  276. Como hemos dicho, lo mejor de este modo gráfico es la cantidad de
  277. colores disponibles: tenemos en nuestra mano una paleta de 256
  278. colores a elegir entre un total de 261.844 colores. Esto es debido a
  279. que cada color lo podemos descomponer en diferentes intensidades de
  280. los colores primarios rojo, verde y azul. Estas intensidades pueden
  281. tomar valores del 0 al 63 para cada color primario, lo que nos da un
  282. total de 64 x 64 x 64 combinaciones diferentes, que es igual a 261.844
  283. colores. Por ejemplo, el rojo intenso tendra unos componentes de
  284. color de 63 para el tono rojo y de 0 para los componentes verde y
  285. azul.
  286.  
  287. Manipulando estos valores podemos conseguir la perfecta representación
  288. de una imagen en pantalla, así como interesantes efectos entre los que
  289. cabe destacar los fundidos, tan comunes en los juegos.
  290.  
  291. Pero ahora llega la gran pregunta, de qué forma se pueden leer y
  292. modifcar los valores de la paleta. Pues es muy sencillo. Para leer
  293. un valor de la paleta hay que introducir el número del color que
  294. queremos leer en el puerto 3C7h, y después leer los valores de rojo,
  295. verde y azul respectivamente del puerto 3C9h. En Turbo Pascal los
  296. números en hexadecimal se introducen con un «$» delante, así el puerto
  297. 3C7h lo pondremos como $3c7, mientras que en «C» y «C++» los números
  298. en hexadecimal se introducen con el prefijo «0x»: por ejemplo, el
  299. mismo número lo escribiremos como 0x3C7.
  300.  
  301. Para que quede más claro, en Turbo Pascal tendremos un «procedure»
  302. para leer el valor de un puerto, que quedará como sigue:
  303.  
  304. Procedure CogeColor(Color : Byte; Var R,V,A : Byte);
  305.  
  306. { Lee las intensidades de Rojo, Verde y Azul de un Color }
  307.  
  308. Begin
  309.  
  310.  Port[$3c7] := Color;
  311.  
  312.  R := Port[$3c9];
  313.  
  314.  V := Port[$3c9];
  315.  
  316.  A := Port[$3c9];
  317.  
  318. End;
  319.  
  320. Ahora vamos a explicar el proceso contrario, es decir, la forma de
  321. modificar las intensidades de rojo, verde y azul de un color
  322. cualquiera. Para ello hay que introducir en el puerto 3C8h el número
  323. del color a modificar y después introducir los valores de rojo, verde
  324. y azul respectivamente en el puerto 3C9h. De nuevo la «procedure»
  325. quedaría como sigue:
  326.  
  327. Procedure PonColor(Color : Byte; R,G,B : Byte);
  328.  
  329. { Esto asigna a un Color las intensidades de Rojo, Verde y Azul }
  330.  
  331. Begin
  332.  
  333.  Port[$3c8] := Color;
  334.  
  335.  Port[$3c9] := R;
  336.  
  337.  Port[$3c9] := G;
  338.  
  339.  Port[$3c9] := B;
  340.  
  341. End;
  342.  
  343. En Turbo C empleamos el comando «inportb( int port )» para leer un
  344. byte del puerto «port», mientras que para escribir a un puerto usamos
  345. «outportb( int port, unsigned char byte )». Ambos comandos pertenecen
  346. a la librería «dos.h».
  347.  
  348.  
  349. PROGRAMACION DE LA VGA 320x200x256 (III)
  350.  
  351. Ha llegado la hora de poner en práctica algunos de los conocimientos
  352. que os hemos venido dando en las dos primeras «lecciones» sobre la
  353. VGA. Ya hemos aprendido a inicializar el modo gráfico 320x200x256, a
  354. iluminar un punto o pixel con un determinado color y a modificar las
  355. intensidades de rojo, verde y azul de cada uno de los 256 colores.
  356.  
  357. Vamos a realizar uno de los efectos más sencillos y a la vez más
  358. llamativos, el conocido como fade o fundido. Este efecto habitual en
  359. todo tipo de juegos consiste en pasar la imagen que tenemos en
  360. pantalla de una forma suave a tener la pantalla totalmente negra (o
  361. cualquier otro color). Y en el efecto contrario, pasar de una
  362. pantalla negra a la imagen con todo su color.
  363.  
  364. Para efectuar los fundidos emplearemos los procedimientos «CogeColor»
  365. y «PonColor» que ya explicamos anteriormente.
  366.  
  367. Procedure PonColor (Color:Byte; R,G,B:Byte);
  368.  
  369. Begin
  370.  
  371.  Port[$3c8]:=Color; Port[$3c9]:=R; Port[$3c9]:=G; Port[$3c9]:=B;
  372.  
  373. End;
  374.  
  375. Procedure CogeColor (Color:Byte; Var R,G,B:Byte);
  376.  
  377. Begin
  378.  
  379.  Port[$3c7]:=Color; R:=Port[$3c9]; G:=Port[$3c9]; B:=Port[$3c9];
  380.  
  381. End;
  382.  
  383. Es posible dejar la pantalla en negro si se ponen los componentes RGB
  384. de todos los colores a 0. El procedimiento PonerNegro hace esto
  385. mismo:
  386.  
  387. Procedure PonerNegro;
  388.  
  389. Var i:Integer;
  390.  
  391. Begin
  392.  
  393.  For i:=0 to 255 do
  394.  
  395.  PonColor (i,0,0,0);
  396.  
  397. End;
  398.  
  399. Para conseguir un buen efecto de fundido lo primero que debemos hacer
  400. es grabar toda la paleta, para ello emplearemos un array:
  401.  
  402. Var Paleta := Array [0..255,1..3] of Byte;
  403.  
  404. Type Pal: Record
  405.  
  406.  Rojo, verde, Azul : byte;
  407.  
  408.  End;
  409.  
  410. Var Paleta : Array [0..255] Of Pal;
  411.  
  412. Los valores de 0 a 255 son para los 256 colores de que consta el modo
  413. VGA que nos trata. Los valores 1 al 3 son para guardar las
  414. intensidades de rojo, verde y azul. Para grabar todos los colores de
  415. la paleta usaremos el siguiente procedimiento:
  416.  
  417. Procedure GrabaPaleta;
  418.  
  419. Var i: Integer;
  420.  
  421. Begin
  422.  
  423.  For i := 0 to 255 do
  424.  
  425.  CogeColor (Loop, Paleta [i,1], Paleta [i,2], Paleta [i,3]);
  426.  
  427. End;
  428.  
  429. Con esto conseguimos grabar la paleta entera en la variable Paleta.
  430. Ahora podemos efectuar cualquier fundido sobre la pantalla. Para ello
  431. podemos emplear el siguiente procedimiento, que efectúa un fundido de
  432. la paleta actual a la que le indiquemos en la variable Paleta. Lo que
  433. hace el programa es ir comparando los valores actuales de Paleta con
  434. los de la paleta que queremos obtener; en caso de ser menores los
  435. incrementa.
  436.  
  437. Procedure FadeToPal;
  438.  
  439. Var i,j: Integer;
  440.  
  441.  Tmp : Array [1..3] of byte;
  442.  
  443.  { Esta variable almacena temporalmente los valores de cada color }
  444.  
  445. Begin
  446.  
  447.  For i:=1 to 64 do Begin
  448.  
  449.  { Los valores de rojo, verde y azul para cada color toman valores
  450.  del 0 al 63, por eso sólo necesitamos ejecutar el bucle un máximo
  451.  de 64 veces}
  452.  
  453.  For j:=0 to 255 do Begin
  454.  
  455.  CogeColor (j,Tmp[1],Tmp[2],Tmp[3]);
  456.  
  457.  If Tmp[1]<Paleta[j,1] then inc (Tmp[1]);
  458.  
  459.  If Tmp[2]<Paleta[j,2] then inc (Tmp[2]);
  460.  
  461.  If Tmp[3]<Paleta[j,3] then inc (Tmp[3]);
  462.  
  463.  { Si las componentes de rojo, verde o azul de cada color «j» son
  464.  menores del valor que debemos alcanzar las incrementamos en 1 }
  465.  
  466.  PonColor (j,Tmp[1],Tmp[2],Tmp[3]);
  467.  
  468.  { Fija los nuevos valores para los colores }
  469.  
  470.  End;
  471.  
  472.  End;
  473.  
  474. End;
  475.  
  476. Pero otro de los efectos clásicos es el fundido de una imagen hasta
  477. alcanzar el negro. El efecto es el mismo pero al revés, así que lo
  478. que tendremos que invertir es el interior del bucle para que si los
  479. valores son mayores de 0, ir restando hasta alcanzar el 0.
  480.  
  481. Procedure FadeToNegro;
  482.  
  483. VAR i,j:integer;
  484.  
  485.  Tmp : Array [1..3] of byte;
  486.  
  487.  { Esta variable almacena temporalmente los valores de cada color }
  488.  
  489. BEGIN
  490.  
  491.  For i:=1 to 64 do Begin
  492.  
  493.  For j:=0 to 255 do Begin
  494.  
  495.  CogeColor (j,Tmp[1],Tmp[2],Tmp[3]);
  496.  
  497.  If Tmp[1]>0 then dec (Tmp[1]);
  498.  
  499.  If Tmp[2]>0 then dec (Tmp[2]);
  500.  
  501.  If Tmp[3]>0 then dec (Tmp[3]);
  502.  
  503.  { Si las componentes de rojo, verde o azul de cada color «j» son
  504.  mayores que 0 les restamos 1, hasta que alcancen este valor }
  505.  
  506.  PonColor (j,Tmp[1],Tmp[2],Tmp[3]);
  507.  
  508.  { Fija los nuevos valores para los colores }
  509.  
  510.  End;
  511.  
  512.  End;
  513.  
  514. End;
  515.  
  516. Si vemos que los fundidos se realizan a gran velocidad podemos poner
  517. un delay para que sean más lentos y logren un mayor efecto. Realizar
  518. un fundido al negro de la pantalla es mucho más impresionante que
  519. limpiar simplemente la pantalla. Para restaurar la paleta original
  520. simplemente hay que poner de nuevo los valores de las componentes
  521. rojo, verde y azul de cada uno de los 256 colores. Lo podemos
  522. conseguir de la siguiente forma:
  523.  
  524. Procedure RestauraPaleta;
  525.  
  526. Var i:integer;
  527.  
  528. Begin
  529.  
  530.  For i:=0 to 255 do
  531.  
  532.  PonColor (i, Paleta[i,1], Paleta [i,2], Paleta [i,3]);
  533.  
  534. End;
  535.  
  536. A lo largo de la explicación hemos considerado la variable Paleta como
  537. un array de 256x3 (256 colores x 3 componentes). Otra forma de
  538. tratar la paleta puede ser definiendo:
  539.  
  540. Type Pal: Record
  541.  
  542.  Rojo, Verde, Azul : Byte;
  543.  
  544.  End;
  545.  
  546. Var Paleta : Array [0..255] Of Pal;
  547.  
  548. De esta forma en vez de referirnos a Paleta [i,1], Paleta [i,2] y
  549. Paleta [i,3] pondremos Paleta[i].Rojo, Paleta[i].Verde y Paleta
  550. [i].Azul, con lo cual evidentemente ganamos en claridad y comodidad.
  551.  
  552.  
  553.  
  554. MEJORAR LA PRESENTACION
  555.  
  556. Para mejorar la presentación de muchos programas, en especial los
  557. juegos, es conveniente adaptarlos a la velocidad del procesador,
  558. aunque antes deberemos saber qué procesador tiene instalado la máquina
  559. sobre la que funcionará el programa.
  560.  
  561. Entre los programadores del Laboratorio Técnico hemos elgido la rutina
  562. que os adjuntamos por su facilidad de uso e implementación en
  563. diferentes lenguajes. Aquí damos un ejemplo de cómo hacerlo en Turbo
  564. Pascal.
  565.  
  566. El bloque principal es el siguiente programa en ensamblador, que
  567. grabaremos como «chiptype.asm»:
  568.  
  569.  CODE SEGMENT BYTE PUBLIC 'CODE'
  570.  
  571.  ASSUME CS:CODE
  572.  
  573.  PUBLIC chiptype
  574.  
  575. chiptype PROC FAR
  576.  
  577. control EQU WORD PTR [bp-2]
  578.  
  579.  push BP
  580.  
  581.  mov BP,SP
  582.  
  583.  push DI
  584.  
  585.  push SI
  586.  
  587.  push CX
  588.  
  589.  call cpu_type
  590.  
  591.  cmp DX,484
  592.  
  593.  jnz Not_486
  594.  
  595.  mov AX,DX
  596.  
  597.  jmp short Got_486
  598.  
  599. Not_486:
  600.  
  601.  call ndp_type
  602.  
  603.  add AX,DX
  604.  
  605.  
  606. Got_486:
  607.  
  608.  pop CX
  609.  
  610.  pop SI
  611.  
  612.  pop DI
  613.  
  614.  pop BP
  615.  
  616.  ret
  617.  
  618. chiptype endp
  619.  
  620. cpu_type PROC NEAR
  621.  
  622.  pushf
  623.  
  624.  xor DX,DX
  625.  
  626.  xor AX,AX
  627.  
  628.  push AX
  629.  
  630.  popf
  631.  
  632.  pushf
  633.  
  634.  pop AX
  635.  
  636.  and AX, 0f000h
  637.  
  638.  cmp AX, 0f000h
  639.  
  640.  jz dig
  641.  
  642.  mov AX, 07000h
  643.  
  644.  push AX
  645.  
  646.  popf
  647.  
  648.  pushf
  649.  
  650.  pop AX
  651.  
  652.  and AX,07000h
  653.  
  654.  jnz got386
  655.  
  656.  mov DX, 0280
  657.  
  658.  jmp SHORT CPUbye
  659.  
  660. got386:
  661.  
  662.  .386
  663.  
  664.  mov edx,esp
  665.  
  666.  and esp, not 3
  667.  
  668.  pushfd
  669.  
  670.  pop eax
  671.  
  672.  mov ecx,eax
  673.  
  674.  xor eax,40000h
  675.  
  676.  push eax
  677.  
  678.  popfd
  679.  
  680.  pushfd
  681.  
  682.  pop eax
  683.  
  684.  xor eax,ecx
  685.  
  686.  shr eax,18
  687.  
  688.  push ecx
  689.  
  690.  popfd
  691.  
  692.  mov esp,edx
  693.  
  694.  and eax,1
  695.  
  696.  .8086
  697.  
  698.  jz SHORT CPU386
  699.  
  700.  mov DX,484
  701.  
  702.  jmp SHORT CPUbye
  703.  
  704. CPU386:
  705.  
  706.  mov DX,380
  707.  
  708.  jmp SHORT CPUbye
  709.  
  710. dig: mov AX, 0ffffh
  711.  
  712.  mov CL, 33
  713.  
  714.  shl AX, CL
  715.  
  716.  jz digmor
  717.  
  718.  mov DX,0180
  719.  
  720.  jmp SHORT CPUbye
  721.  
  722. digmor: xor AL,AL
  723.  
  724.  mov AL,40h
  725.  
  726.  mul AL
  727.  
  728.  jz gotNEC
  729.  
  730.  mov DX,0080
  731.  
  732.  jmp SHORT CPUbye
  733.  
  734. gotNEC: mov DX,0200
  735.  
  736. CPUbye: popf
  737.  
  738.  ret
  739.  
  740. cpu_type endp
  741.  
  742. ndp_type PROC NEAR
  743.  
  744. do_we: fninit
  745.  
  746.  mov byte ptr control+1,0
  747.  
  748.  fnstcw control
  749.  
  750.  mov AH,byte ptr [control+1]
  751.  
  752.  cmp AH,03h
  753.  
  754.  je chk_87
  755.  
  756.  xor AX,AX
  757.  
  758.  jmp SHORT NDPbye
  759.  
  760. chk_87: and control,NOT 0080h
  761.  
  762.  fldcw control
  763.  
  764.  fdisi
  765.  
  766.  fstcw control
  767.  
  768.  test control,0080h
  769.  
  770.  jz chk287
  771.  
  772.  mov AX,0001
  773.  
  774.  jmp SHORT NDPbye
  775.  
  776. chk287: finit
  777.  
  778.  fld1
  779.  
  780.  fldz
  781.  
  782.  fdiv
  783.  
  784.  fld st
  785.  
  786.  fchs
  787.  
  788.  fcompp
  789.  
  790.  fstsw control
  791.  
  792.  fwait
  793.  
  794.  mov AX,control
  795.  
  796.  sahf
  797.  
  798.  jnz got387
  799.  
  800.  mov AX,0002
  801.  
  802.  jmp SHORT NDPbye
  803.  
  804. got387: mov AX,0003
  805.  
  806. NDPbye: ret
  807.  
  808.  
  809. ndp_type endp
  810.  
  811. CODE ends
  812.  
  813.  end
  814.  
  815. Una vez copiado lo ensamblamos con la orden «Tasm chiptype.asm», lo
  816. que nos generará el codigo objeto de este programa. Incluye la
  817. función «Chiptype» que devuelve un valor entero dependiendo del
  818. procesador sobre el que corra el programa: mayor que 480 si es un
  819. 486, entre 380 y 480 si es un 386, si es un 286 nos da valores entre
  820. 280 y 380, mientra que si el valor devuelto es menor que 280 el
  821. procesador sera un 808x ó 8018x. Para comprobar si existe
  822. coprocesador instalado basta con hallar el valor de «ChipType» en
  823. módulo 10 y comprobar que es distinto de 0.
  824.  
  825. Para implementar esta rutina en Turbo Pascal basta con escribir el
  826. siguiente programa, que nos creará una «unidad» que podremos emplear
  827. desde el programa principal.
  828.  
  829. {$R-,S-,I-,D+,F-,V-,B-,N-,L+ }
  830.  
  831. UNIT Chips;
  832.  
  833. INTERFACE
  834.  
  835. function ChipType : integer;
  836.  
  837. IMPLEMENTATION
  838.  
  839. {$L ChipType.obj }
  840.  
  841. function ChipType; external;
  842.  
  843. END.
  844.  
  845. Este es el programa principal, el cual nos devuelve el
  846. procesador con el que estamos trabajando:
  847.  
  848.  
  849. PROGRAM cpucheck;
  850.  
  851. USES
  852.  
  853.  Chips;
  854.  
  855. var
  856.  
  857.  Chip_Value : integer;
  858.  
  859. BEGIN
  860.  
  861.  Chip_Value := ChipType;
  862.  
  863.  if Chip_Value > 480 then
  864.  
  865.  Write ( 'El procesador es un 486' )
  866.  
  867.  else
  868.  
  869.  if Chip_Value > 380 then
  870.  
  871.  Write ( 'El procesador es un 386' )
  872.  
  873.  else
  874.  
  875.  if Chip_Value > 280 then
  876.  
  877.  Write ( 'El procesador es un 286' )
  878.  
  879.  else
  880.  
  881.  Write ( 'El procesador es un 808x o 8018x' );
  882.  
  883.  if Chip_Value > 480 then
  884.  
  885.  Writeln
  886.  
  887.  else
  888.  
  889.  if Chip_Value mod 10 <> 0 then
  890.  
  891.  Writeln ( ' Con Coprocesador' )
  892.  
  893.  else
  894.  
  895.  Writeln;
  896.  
  897. END.
  898.  
  899.  
  900.  
  901. EL ICONO DEL RATON
  902.  
  903. Entre las consultas que más suelen llegar a nuestra redacción se
  904. encuentran las referentes a la programación del ratón. Una de las
  905. dudas más comunes se refiere a la manera de modificar el icono del
  906. mismo. Mediante esta sencilla rutina en ensamblador se puede
  907. modificar la clásica flecha por cualquier otro dibujo. El único
  908. límite está en que éste deberá tener un tamaño de 16x16 pixels y sólo
  909. podrá tener un color.
  910.  
  911. Con el fin de facilitar su implementación, hemos incluido el código
  912. ensamblador dentro del siguiente procedimiento, realizado en Turbo
  913. Pascal.
  914.  
  915. procedure MiNuevoCursor; assembler;
  916.  
  917. asm
  918.  
  919.  jmp @1
  920.  
  921.  dw 1111111111111111b { máscara de pantalla : }
  922.  
  923.  dw 1111111111111111b { los 1's son pixels que }
  924.  
  925.  dw 1111100000001111b { "transparentan", los 0's }
  926.  
  927.  dw 1111100000001111b { son pixels donde el cursor }
  928.  
  929.  dw 1111000111000111b { es opaco. }
  930.  
  931.  dw 1100000111000001b
  932.  
  933.  dw 1100001111100001b
  934.  
  935.  dw 1100111111111001b
  936.  
  937.  dw 1100111111111001b
  938.  
  939.  dw 1100001111100001b
  940.  
  941.  dw 1100000111000001b
  942.  
  943.  dw 1111000111000111b
  944.  
  945.  dw 1111100000001111b
  946.  
  947.  dw 1111100000001111b
  948.  
  949.  dw 1111111111111111b
  950.  
  951.  dw 1111111111111111b
  952.  
  953.  
  954.  dw 0000000000000000b { máscara de cursor: }
  955.  
  956.  dw 0000000000000000b { los 1's son pixels que }
  957.  
  958.  dw 0000011111110000b { se encienden para dibujar }
  959.  
  960.  dw 0000011111110000b { el cursor. }
  961.  
  962.  dw 0000111000110000b
  963.  
  964.  dw 0011111000111110b
  965.  
  966.  dw 0011110000011110b
  967.  
  968.  dw 0011000000000110b
  969.  
  970.  dw 0011000000000110b
  971.  
  972.  dw 0011110000011110b
  973.  
  974.  dw 0011111000111110b
  975.  
  976.  dw 0000111000111000b
  977.  
  978.  dw 0000011111110000b
  979.  
  980.  dw 0000011111110000b
  981.  
  982.  dw 0000000000000000b
  983.  
  984.  dw 0000000000000000b
  985.  
  986.  
  987. @1:
  988.  
  989.  mov ax, cs
  990.  
  991.  mov es, ax
  992.  
  993.  mov dx, offset MiCursor+2
  994.  
  995.  mov ax, $0009
  996.  
  997.  mov bx,8 { coordenada x de control }
  998.  
  999.  mov cx,8 { coordenada y de control }
  1000.  
  1001.  int $33
  1002.  
  1003. end;
  1004.  
  1005. Como se puede observar, necesitamos dos máscaras (todos los conjuntos
  1006. de unos y ceros que representan el icono), ambas de 16x16. En la
  1007. «máscara de pantalla» los unos (1) representan los pixels que quedan
  1008. trasparentes mientras que los ceros (0) son los pixele donde el cursor
  1009. queda opaco. La segunda máscara es la «máscara del cursor», en la que
  1010. todo ocurre al revés que en la anterior; es decir, basta con sustituir
  1011. los unos por ceros y los ceros por unos, para conseguir esta segunda
  1012. máscara. En esta, los unos son los pixels que se encienden para
  1013. dibujar el icono, mientras que los ceros representan las zonas
  1014. traparentes.
  1015.  
  1016. También habrá que tener en cuenta las coordenadas del punto de
  1017. control, es decir, el punto sobre el que se dan las coordenadas del
  1018. ratón. Si tenemos una flecha, el punto de control será la punta de la
  1019. misma. En el ejemplo presentado, una especie de círculo, el punto de
  1020. control se situa en el centro del círculo.
  1021.  
  1022.  
  1023.  
  1024. IDENTIFICAR EL PROCESADOR
  1025.  
  1026. Para mejorar la presentación de muchos programas, en especial los
  1027. juegos, es conveniente adaptar el programa a la velocidad del
  1028. procesador, pero para ello debemos saber qué chip tiene instalado la
  1029. máquina sobre la que funcionará el programa. Hace dos meses, en la
  1030. sección de «Microconsultas» ya indicamos una rutina en ensamblador que
  1031. servía para estos menesteres.
  1032.  
  1033. Pero como dicha rutina podía resultar difícil de implementar en
  1034. nuestras aplicaciones, entre los programadores del Laboratorio Técnico
  1035. hemos elegido la siguiente rutina. Sus características fundamentales
  1036. son su facilidad de uso e implementación en diferentes lenguajes.
  1037. Aquí vamos a daros un ejemplo de cómo hacerlo en Turbo Pascal.
  1038.  
  1039. El bloque principal es el siguiente programa en ensamblador, que
  1040. grabaremos como «chiptype.asm»:
  1041.  
  1042. CODE SEGMENT BYTE PUBLIC 'CODE'
  1043.  
  1044.  ASSUME CS:CODE
  1045.  
  1046.  PUBLIC chiptype
  1047.  
  1048. chiptype PROC FAR
  1049.  
  1050. control EQU WORD PTR [bp-2]
  1051.  
  1052.  push BP
  1053.  
  1054.  mov BP,SP
  1055.  
  1056.  push DI
  1057.  
  1058.  push SI
  1059.  
  1060.  push CX
  1061.  
  1062.  call cpu_type
  1063.  
  1064.  cmp DX,484
  1065.  
  1066.  jnz Not_486
  1067.  
  1068.  mov AX,DX
  1069.  
  1070.  jmp short Got_486
  1071.  
  1072. Not_486:
  1073.  
  1074.  call ndp_type
  1075.  
  1076.  add AX,DX
  1077.  
  1078.  
  1079. Got_486:
  1080.  
  1081.  pop CX
  1082.  
  1083.  pop SI
  1084.  
  1085.  pop DI
  1086.  
  1087.  pop BP
  1088.  
  1089.  ret
  1090.  
  1091. chiptype endp
  1092.  
  1093. cpu_type PROC NEAR
  1094.  
  1095.  pushf
  1096.  
  1097.  xor DX,DX
  1098.  
  1099.  xor AX,AX
  1100.  
  1101.  push AX
  1102.  
  1103.  popf
  1104.  
  1105.  pushf
  1106.  
  1107.  pop AX
  1108.  
  1109.  and AX, 0f000h
  1110.  
  1111.  cmp AX, 0f000h
  1112.  
  1113.  jz dig
  1114.  
  1115.  mov AX, 07000h
  1116.  
  1117.  push AX
  1118.  
  1119.  popf
  1120.  
  1121.  pushf
  1122.  
  1123.  pop AX
  1124.  
  1125.  and AX,07000h
  1126.  
  1127.  jnz got386
  1128.  
  1129.  mov DX, 0280
  1130.  
  1131.  jmp SHORT CPUbye
  1132.  
  1133. got386:
  1134.  
  1135.  .386
  1136.  
  1137.  mov edx,esp
  1138.  
  1139.  and esp, not 3
  1140.  
  1141.  pushfd
  1142.  
  1143.  pop eax
  1144.  
  1145.  mov ecx,eax
  1146.  
  1147.  xor eax,40000h
  1148.  
  1149.  push eax
  1150.  
  1151.  popfd
  1152.  
  1153.  pushfd
  1154.  
  1155.  pop eax
  1156.  
  1157.  xor eax,ecx
  1158.  
  1159.  shr eax,18
  1160.  
  1161.  push ecx
  1162.  
  1163.  popfd
  1164.  
  1165.  mov esp,edx
  1166.  
  1167.  and eax,1
  1168.  
  1169.  .8086
  1170.  
  1171.  jz SHORT CPU386
  1172.  
  1173.  mov DX,484
  1174.  
  1175.  jmp SHORT CPUbye
  1176.  
  1177. CPU386:
  1178.  
  1179.  mov DX,380
  1180.  
  1181.  jmp SHORT CPUbye
  1182.  
  1183. dig: mov AX, 0ffffh
  1184.  
  1185.  mov CL, 33
  1186.  
  1187.  shl AX, CL
  1188.  
  1189.  jz digmor
  1190.  
  1191.  mov DX,0180
  1192.  
  1193.  jmp SHORT CPUbye
  1194.  
  1195. digmor: xor AL,AL
  1196.  
  1197.  mov AL,40h
  1198.  
  1199.  mul AL
  1200.  
  1201.  jz gotNEC
  1202.  
  1203.  mov DX,0080
  1204.  
  1205.  jmp SHORT CPUbye
  1206.  
  1207. gotNEC: mov DX,0200
  1208.  
  1209. CPUbye: popf
  1210.  
  1211.  ret
  1212.  
  1213. cpu_type endp
  1214.  
  1215. ndp_type PROC NEAR
  1216.  
  1217. do_we: fninit
  1218.  
  1219.  mov byte ptr control+1,0
  1220.  
  1221.  fnstcw control
  1222.  
  1223.  mov AH,byte ptr [control+1]
  1224.  
  1225.  cmp AH,03h
  1226.  
  1227.  je chk_87
  1228.  
  1229.  xor AX,AX
  1230.  
  1231.  jmp SHORT NDPbye
  1232.  
  1233. chk_87: and control,NOT 0080h
  1234.  
  1235.  fldcw control
  1236.  
  1237.  fdisi
  1238.  
  1239.  fstcw control
  1240.  
  1241.  test control,0080h
  1242.  
  1243.  jz chk287
  1244.  
  1245.  mov AX,0001
  1246.  
  1247.  jmp SHORT NDPbye
  1248.  
  1249. chk287: finit
  1250.  
  1251.  fld1
  1252.  
  1253.  fldz
  1254.  
  1255.  fdiv
  1256.  
  1257.  fld st
  1258.  
  1259.  fchs
  1260.  
  1261.  fcompp
  1262.  
  1263.  fstsw control
  1264.  
  1265.  fwait
  1266.  
  1267.  mov AX,control
  1268.  
  1269.  sahf
  1270.  
  1271.  jnz got387
  1272.  
  1273.  mov AX,0002
  1274.  
  1275.  jmp SHORT NDPbye
  1276.  
  1277. got387: mov AX,0003
  1278.  
  1279. NDPbye: ret
  1280.  
  1281.  
  1282. ndp_type endp
  1283.  
  1284. CODE ends
  1285.  
  1286.  end
  1287.  
  1288. Una vez copiado, lo ensamblaremos con la orden «tasm chiptype.asm», lo
  1289. que nos generará el codigo objeto. Este programa incluye la función
  1290. «Chiptype», que devuelve un valor entero dependiendo del procesador
  1291. sobre el que corra: si es un 486 devuelve un valor mayor que 480; si
  1292. es un 386 devuelve un valor entre 380 y 480; si es un 286 nos da
  1293. valores entre 280 y 380; mientras que si el valor devuelto es menor
  1294. que 280, el procesador sera un 808x o 8018x. Para comprobar si existe
  1295. coprocesador instalado basta con hallar el valor de «ChipType» en
  1296. módulo 10 y comprobar que es distinto de 0.
  1297.  
  1298. Para implementar esta rutina en Turbo Pascal basta con escribir la
  1299. siguiente rutina, que nos creará una unidad para ser usada desde el
  1300. programa principal y de esta forma simplificar el proceso.
  1301.  
  1302. {$R-,S-,I-,D+,F-,V-,B-,N-,L+ }
  1303.  
  1304. UNIT Chips;
  1305.  
  1306. INTERFACE
  1307.  
  1308. function ChipType : integer;
  1309.  
  1310. IMPLEMENTATION
  1311.  
  1312. {$L ChipType.obj }
  1313.  
  1314. function ChipType; external;
  1315.  
  1316. END.
  1317.  
  1318. Este es el programa principal, el cual nos devuelve el
  1319. procesador sobre el que está funcionando nuestra particular
  1320. aplicación:
  1321.  
  1322. PROGRAM cpucheck;
  1323.  
  1324. USES
  1325.  
  1326.  Chips;
  1327.  
  1328. var
  1329.  
  1330.  Chip_Value : integer;
  1331.  
  1332. BEGIN
  1333.  
  1334.  Chip_Value := ChipType;
  1335.  
  1336.  if Chip_Value > 480 then
  1337.  
  1338.  Write ( 'El procesador es un 80486' )
  1339.  
  1340.  else
  1341.  
  1342.  if Chip_Value > 380 then
  1343.  
  1344.  Write ( 'El procesador es un 80386' )
  1345.  
  1346.  else
  1347.  
  1348.  if Chip_Value > 280 then
  1349.  
  1350.  Write ( 'El procesador es un 80286' )
  1351.  
  1352.  else
  1353.  
  1354.  Write ( 'El procesador es un 808x o 8018x' );
  1355.  
  1356.  if Chip_Value > 480 then
  1357.  
  1358.  Writeln
  1359.  
  1360.  else
  1361.  
  1362.  if Chip_Value mod 10 <> 0 then
  1363.  
  1364.  Writeln ( ' Con Coprocesador' )
  1365.  
  1366.  else
  1367.  
  1368.  Writeln;
  1369.  
  1370. END.
  1371.  
  1372.  
  1373. PARAMETROS DE DISCOS DUROS
  1374.  
  1375. Muchos usuarios tras un borrado accidental de los datos de la BIOS, o
  1376. cualquier circunstancia similar, pierden todo el acceso a su disco
  1377. duro. En estos casos sólo es posible recuperar los datos de nuestro
  1378. disco si conseguimos reconfigurar correctamente los parámetros que
  1379. deben ir en la BIOS. El problema surge cuando no tenemos apuntados
  1380. dichos valores, precaución que deberíamos tomar siempre que
  1381. comprásemos un disco duro. Y probar una a una todas las combinaciones
  1382. posibles de cilindros, cabezas y sectores puede resultar una labor
  1383. imposible. Además, ya se sabe que los problemas sólo surgen cuando no
  1384. pueden ser solucionados.
  1385.  
  1386. El siguiente programita, escrito en C, nos dará todos los datos de
  1387. nuestro disco duro, proporcionando tanto cilindros como cabezas y
  1388. sectores, dando tanto los que lee de la BIOS como los que lee del
  1389. propio disco duro.
  1390.  
  1391. #define _PCACTUAL_
  1392.  
  1393. #include <stdlib.h>
  1394.  
  1395. #include <dos.h>
  1396.  
  1397. #include <stdio.h>
  1398.  
  1399. #include <conio.h>
  1400.  
  1401. #include <bios.h>
  1402.  
  1403. char *getascii (unsigned int in_data [], int off_start, int off_end);
  1404.  
  1405. void main (void)
  1406.  
  1407. {
  1408.  
  1409.  unsigned int dd [256];
  1410.  
  1411.  unsigned int dd_off;
  1412.  
  1413.  unsigned int loop;
  1414.  
  1415.  int num_drv; /* Numero de discos duros */
  1416.  
  1417.  union REGS registers;
  1418.  
  1419.  unsigned int bios_cyl [2], bios_head [2], bios_sec [2];
  1420.  
  1421.  /* Cilindros, Cabezas, Sectores */
  1422.  
  1423.  clrscr ();
  1424.  
  1425.  /* Cuantos discos y parámetros */
  1426.  
  1427.  num_drv = peekb (0x40, 0x75);
  1428.  
  1429.  /* area de datos de la BIOS, Numero de discos duros */
  1430.  
  1431.  /* Byte en el segmento 40H Offset 75H */
  1432.  
  1433.  for (loop = 0; loop < num_drv; loop++)
  1434.  
  1435.  /* Buscar a través de los discos */
  1436.  
  1437.  {
  1438.  
  1439.  /* Coger información del disco IDE */
  1440.  
  1441.  while (inp (0x1F7) != 0x50);
  1442.  
  1443.  /* Espera a que la controladora no esté ocupada */
  1444.  
  1445.  outp (0x1F6, (loop == 0 ? 0xA0 : 0xB0));
  1446.  
  1447.  /* Coger primer/segundo disco */
  1448.  
  1449.  outp (0x1F7, 0xEC);
  1450.  
  1451.  /* Coger información del disco */
  1452.  
  1453.  while (inp (0x1F7) != 0x58);
  1454.  
  1455.  /* Esperar a que esté listo para datos */
  1456.  
  1457.  for (dd_off = 0; dd_off != 256; dd_off++) /* Leer "sector" */
  1458.  
  1459.  dd [dd_off] = inpw (0x1F0);
  1460.  
  1461.  /* Coger información del disco de la BIOS */
  1462.  
  1463.  registers.h.ah = 0x8;
  1464.  
  1465.  /* Coger información del disco */
  1466.  
  1467.  registers.h.dl = 0x80 + loop;
  1468.  
  1469.  /* Disco es 80H para el disco 0, 81H para el 1 */
  1470.  
  1471.  int86 (0x13, ®isters, ®isters);
  1472.  
  1473.  if (! registers.x.cflag)
  1474.  
  1475.  {
  1476.  
  1477.  bios_head [loop] = registers.h.dh + 1;
  1478.  
  1479.  /* Las cabezas desde el 0 */
  1480.  
  1481.  bios_sec [loop] = registers.h.cl & 0x3F;
  1482.  
  1483.  /* sectores es bits 5 - 0 */
  1484.  
  1485.  bios_cyl [loop] = ((registers.h.cl & 0xC0) << 2) +
  1486.  registers.h.ch + 2;
  1487.  
  1488.  }
  1489.  
  1490.  clrscr ();
  1491.  
  1492.  fprintf (stdout, "DISCO %d:\n", loop);
  1493.  
  1494.  fprintf (stdout, "Número de modelo__________: %s\n", getascii (dd, 27, 46));
  1495.  
  1496.  fprintf (stdout, "Número de serie___________: %s\n", getascii (dd, 10, 19));
  1497.  
  1498.  fprintf (stdout, "Resultados del disco duro\n");
  1499.  
  1500.  fprintf (stdout, "Numero de cilindros (Fijado)______: %6u\n", dd [1]);
  1501.  
  1502.  fprintf (stdout, "Numero de cabezas_________________: %6u\n", dd [3]);
  1503.  
  1504.  fprintf (stdout, "Numero de sectores por pista______: %6u\n\n", dd [6]);
  1505.  
  1506.  fprintf (stdout, "Resultados de la BIOS\n");
  1507.  
  1508.  fprintf (stdout, "Numero de cilindros____________: %6u\n", bios_cyl [loop]);
  1509.  
  1510.  fprintf (stdout, "Numero de cabezas_____________: %6u\n", bios_head [loop]);
  1511.  
  1512.  fprintf (stdout, "Numero de sectores por pista__: %6u\n\n", bios_sec [loop]);
  1513.  
  1514.  if (! loop)
  1515.  
  1516.  {
  1517.  
  1518.  fprintf (stdout, "Pulsa una tecla");
  1519.  
  1520.  getch ();
  1521.  
  1522.  }
  1523.  
  1524.  }
  1525.  
  1526. }
  1527.  
  1528. char *getascii (unsigned int in_data [], int off_start, int off_end)
  1529.  
  1530. {
  1531.  
  1532.  static char ret_val [255];
  1533.  
  1534.  int loop, loop1;
  1535.  
  1536.  for (loop = off_start, loop1 = 0; loop <= off_end; loop++)
  1537.  
  1538.  {
  1539.  
  1540.  ret_val [loop1++] = (char) (in_data [loop] / 256); /* Coge el byte alto */
  1541.  
  1542.  ret_val [loop1++] = (char) (in_data [loop] % 256); /* Coge el byte bajo */
  1543.  
  1544.  }
  1545.  
  1546.  ret_val [loop1] = '\0';
  1547.  
  1548.  /* Nos aseguramos de que termina en carácter nulo */
  1549.  
  1550.  return (ret_val);
  1551.  
  1552. }
  1553.  
  1554.  
  1555. «SHELLS» LIMPIOS
  1556.  
  1557. Cuando escribimos un programa que efectúa llamadas externas a otras
  1558. aplicaciones necesitamos usar la función «Exec(Nombre_de_programa,
  1559. Linea_de_comandos». Esto funciona perfectamente, pero nos podemos
  1560. encontrar con un grave incoveniente: la salida se redirecciona a la
  1561. pantalla, escribiendo mensajes que en ocasiones no deseamos que se
  1562. vean. Un ejemplo lo encontramos cuando descomprimimos un programa,
  1563. llamamos al descompresor con «Exec» y en pantalla se nos muestra toda
  1564. la información del proceso de descompresión.
  1565.  
  1566. En estas ocasiones quedaría más elegante presentar en pantalla un
  1567. mensaje como «Trabajando...», «Instalando...» o algo similar.
  1568. Podremos conseguir este aspecto, sin duda mucho más profesional,
  1569. mediante el uso de la siguiente unidad, escrita en Turbo Pascal (como
  1570. se hace uso del ensamblador incorporado es necesario la versión 6.0 ó
  1571. superior). A pesar de estar escrita en Turbo Pascal, los usuarios del
  1572. lenguaje C, no encontrarán excesivas dificultades para adaptarla a sus
  1573. necesidades.
  1574.  
  1575. unit Mejora_Exec;
  1576.  
  1577. interface
  1578.  
  1579. Uses Dos;
  1580.  
  1581. function SetOutput(Nombre_Fichero: PathStr): Boolean;
  1582.  
  1583. procedure CancelOutput;
  1584.  
  1585. implementation
  1586.  
  1587. const OutRedir: Boolean = False;
  1588.  
  1589. function SetOutput(Nombre_Fichero: PathStr): Boolean;
  1590.  
  1591. begin
  1592.  
  1593.  Nombre_Fichero:=Nombre_Fichero+#0;
  1594.  
  1595.  SetOutput:=False;
  1596.  
  1597.  asm
  1598.  
  1599.  push ds
  1600.  
  1601.  mov ax, ss
  1602.  
  1603.  mov ds, ax
  1604.  
  1605.  lea dx, Nombre_Fichero[1]
  1606.  
  1607.  mov ah, 3Ch
  1608.  
  1609.  int 21h
  1610.  
  1611.  pop ds
  1612.  
  1613.  jnc @@1
  1614.  
  1615.  ret
  1616.  
  1617. @@1:
  1618.  
  1619.  push ax
  1620.  
  1621.  mov bx, ax
  1622.  
  1623.  mov cx, Output.FileRec.Handle
  1624.  
  1625.  mov ah, 46h
  1626.  
  1627.  int 21h
  1628.  
  1629.  mov ah, 3Eh
  1630.  
  1631.  pop bx
  1632.  
  1633.  jnc @@2
  1634.  
  1635.  ret
  1636.  
  1637. @@2:
  1638.  
  1639.  int 21h
  1640.  
  1641.  end;
  1642.  
  1643.  OutRedir:=True;
  1644.  
  1645.  SetOutput:=True;
  1646.  
  1647. end;
  1648.  
  1649. procedure CancelOutput;
  1650.  
  1651. var
  1652.  
  1653.  Nombre_Fichero: String[4];
  1654.  
  1655. begin
  1656.  
  1657.  if not OutRedir then Exit;
  1658.  
  1659.  Nombre_Fichero:='CON'#0;
  1660.  
  1661.  asm
  1662.  
  1663.  push ds
  1664.  
  1665.  mov ax, ss
  1666.  
  1667.  mov ds, ax
  1668.  
  1669.  lea dx, Nombre_Fichero[1]
  1670.  
  1671.  mov ax, 3D01h
  1672.  
  1673.  int 21h
  1674.  
  1675.  pop ds
  1676.  
  1677.  jnc @@1
  1678.  
  1679.  ret
  1680.  
  1681. @@1:
  1682.  
  1683.  push ax
  1684.  
  1685.  mov bx, ax
  1686.  
  1687.  mov cx, Output.FileRec.Handle
  1688.  
  1689.  mov ah, 46h
  1690.  
  1691.  int 21h
  1692.  
  1693.  mov ah, 3Eh
  1694.  
  1695.  pop bx
  1696.  
  1697.  int 21h
  1698.  
  1699.  end;
  1700.  
  1701.  OutRedir:=False;
  1702.  
  1703. end;
  1704.  
  1705. end.
  1706.  
  1707. Esta unidad nos permitirá redireccionar la salida estándar a cualquier
  1708. fichero (en vez de al monitor), dado por la variable «Nombre_Fichero».
  1709. Esta variable puede incluso tomar el valor NUL, en cuyo caso no se
  1710. producirá ninguna salida, ni a fichero ni a pantalla.
  1711.  
  1712. En el programa principal haremos:
  1713.  
  1714. SetOutput('NUL');
  1715.  
  1716. Exec(....);
  1717.  
  1718. CancelOutput;
  1719.  
  1720.  
  1721.  
  1722.